package com.han.http.client.method;

import com.han.http.client.exception.RequestNotExecutedException;
import com.han.http.client.paramter.FormDataPair;
import com.han.http.client.paramter.RequestHeader;
import com.han.http.util.ArrayTool;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.util.*;
import java.util.zip.GZIPInputStream;

import static com.han.http.util.Print.printlnWarning;

/**
 * @fileName: HttpPost
 * @author: Han
 * @date: 2018/6/22 9:05
 * @dscription: 通过post方法请求url上的资源
 */
public class HttpPost extends BaseHttpRequest {

    /**
     * 目的地址
     */
    private String urlStr;
    /**
     * 代理对象
     */
    private Proxy proxy;
    /**
     * 读取超时时间
     */
    private int readTimeout;
    /**
     * 连接超时时间
     */
    private int connectTimeout;
    /**
     * 是否重定向
     */
    private boolean isRedirects;
    /**
     * 请求头
     */
    private List<RequestHeader> headers = new ArrayList<>();
    /**
     * 表单数据
     */
    private List<FormDataPair> formDataPairs = new ArrayList<>();
    /**
     * 连接对象
     */
    private HttpURLConnection httpURLConnection;

    /**
     * 构造器,只允许通过BaseHttpRequest的静态方法获取
     *
     * @param urlStr url
     */
    HttpPost(String urlStr) {
        this.urlStr = urlStr;
    }

    @Override
    public void execute() throws IOException {

        if (formDataPairs.isEmpty()) {
            printlnWarning("表单为空");
        }
        //根据proxy是否为空,选择实例化HttpURLConnection
        if (Objects.isNull(proxy)) {
            httpURLConnection = (HttpURLConnection) new URL(urlStr).openConnection();
        } else {
            httpURLConnection = (HttpURLConnection) new URL(urlStr).openConnection(proxy);
        }
        //设置请求方式
        httpURLConnection.setRequestMethod("POST");
        //设置连接超时时间
        httpURLConnection.setConnectTimeout(connectTimeout);
        //设置读取超时时间
        httpURLConnection.setReadTimeout(readTimeout);
        //设置是否重定向
        httpURLConnection.setInstanceFollowRedirects(isRedirects);
        //设置请求头
        for (RequestHeader header : headers) {
            String key = header.getKey();
            String value = header.getValue();
            httpURLConnection.setRequestProperty(key, value);
        }
        //设置可以写入
        httpURLConnection.setDoOutput(true);
        //连接表单数据
        StringBuilder formData = new StringBuilder();
        for (FormDataPair tempPair : formDataPairs) {
            formData.append(tempPair).append("&");
        }
        if (formData.length() != 0) {
            //去掉最后一个&
            formData.deleteCharAt(formData.length() - 1);
        }
        //获取输出流
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(httpURLConnection.getOutputStream());
        //写入
        outputStreamWriter.write(formData.toString());
        outputStreamWriter.flush();

    }

    @Override
    public String getHeaderField(String key) throws RequestNotExecutedException {
        if (Objects.isNull(httpURLConnection)) {
            throw new RequestNotExecutedException();
        }
        return httpURLConnection.getHeaderField(key);
    }

    @Override
    public Map<String, List<String>> getHeaderFields() throws RequestNotExecutedException {
        if (Objects.isNull(httpURLConnection)) {
            throw new RequestNotExecutedException();
        }
        return httpURLConnection.getHeaderFields();
    }

    @Override
    public Proxy getProxy() {
        return proxy;
    }

    @Override
    public void setProxy(Proxy proxy) {
        this.proxy = proxy;
    }

    @Override
    public int getReadTimeout() {
        return readTimeout;
    }

    @Override
    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    @Override
    public int getConnectTimeout() {
        return connectTimeout;
    }

    @Override
    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    @Override
    public boolean isRedirects() {
        return isRedirects;
    }

    @Override
    public void setRedirects(boolean redirects) {
        isRedirects = redirects;
    }

    @Override
    public RequestHeader[] getHeaders() {
        return headers.toArray(new RequestHeader[0]);
    }

    @Override
    public void setHeaders(RequestHeader[] headers) {
        this.headers.addAll(Arrays.asList(headers));
    }

    @Override
    public void setHeader(RequestHeader header) {
        this.headers.add(header);
    }

    @Override
    String getTextWithGzip(String charset) throws IOException, RequestNotExecutedException {

        //如果httpURLConnection没有被初始化,抛出异常
        if (Objects.isNull(httpURLConnection)) {
            throw new RequestNotExecutedException();
        }

        StringBuilder text = new StringBuilder();

        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(new GZIPInputStream(
                    httpURLConnection.getInputStream()), charset));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                text.append(line).append("\n");
            }
        } finally {
            try {
                Objects.requireNonNull(bufferedReader).close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return text.toString();
    }

    @Override
    String getTextWithoutGzip(String charset) throws IOException, RequestNotExecutedException {

        //如果httpURLConnection没有被初始化,抛出异常
        if (Objects.isNull(httpURLConnection)) {
            throw new RequestNotExecutedException();
        }

        StringBuilder text = new StringBuilder();

        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), charset));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                text.append(line).append("\n");
            }
        } finally {
            try {
                Objects.requireNonNull(bufferedReader).close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return text.toString();
    }

    @Override
    public void disconnect() throws RequestNotExecutedException {
        //如果httpURLConnection没有被初始化,抛出异常
        if (Objects.isNull(httpURLConnection)) {
            throw new RequestNotExecutedException();
        }

        httpURLConnection.disconnect();
    }

    @Override
    public int getStatusCode() throws RequestNotExecutedException, IOException {

        //如果httpURLConnection没有被初始化,抛出异常
        if (Objects.isNull(httpURLConnection)) {
            throw new RequestNotExecutedException();
        }
        return httpURLConnection.getResponseCode();
    }

    @Override
    public byte[] getBytes() throws IOException, RequestNotExecutedException {
        //如果httpURLConnection没有被初始化,抛出异常
        if (Objects.isNull(httpURLConnection)) {
            throw new RequestNotExecutedException();
        }

        byte[] bytes;
        InputStream inputStream = null;
        try {
            inputStream = httpURLConnection.getInputStream();
            bytes = new byte[inputStream.available()];
            byte[] tempBytes = new byte[1024];
            int count = 0;
            while (inputStream.read(tempBytes) != -1) {
                ArrayTool.copyAToB(bytes, tempBytes.length * count, tempBytes);
                count++;
            }

        } finally {
            try {
                Objects.requireNonNull(inputStream).close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

    /**
     * 获取表单数据
     *
     * @return 表单数据
     */
    public FormDataPair[] getFormDataPairs() {
        return formDataPairs.toArray(new FormDataPair[0]);
    }

    /**
     * 设置表单数据
     *
     * @param formDataPairs 表单数据
     */
    public void setFormDataPairs(FormDataPair[] formDataPairs) {
        this.formDataPairs.addAll(Arrays.asList(formDataPairs));
    }
}
